package com.ruoyi.workflow.service.impl;

import com.alibaba.fastjson2.JSON;
import com.ruoyi.business.domain.ServiceItem;
import com.ruoyi.business.service.IServiceItemService;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.page.TableSupport;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.workflow.domain.CarPackageAudit;
import com.ruoyi.workflow.domain.CarPackageAuditVo;
import com.ruoyi.workflow.service.IBpmnNodeService;
import com.ruoyi.workflow.service.ICarPackageAuditService;
import com.ruoyi.workflow.service.ITaskToDoService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class TaskToDoServiceImpl implements ITaskToDoService {
    @Autowired
    private TaskService taskService;
    @Autowired
    private ICarPackageAuditService carPackageAuditService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private ISysUserService userService;
    @Autowired
    private IBpmnNodeService bpmnNodeService;
    @Autowired
    private IServiceItemService serviceItemService;
    @Transactional(rollbackFor = Exception.class)
    @Override
    public TableDataInfo selectTodoList(CarPackageAudit carPackageAudit) {
        // 1. 创建返回分页数据对象
        TableDataInfo info = new TableDataInfo();
        info.setCode(HttpStatus.SUCCESS);
        info.setMsg("查询成功");
        // 2. 获取当前登录用户的 id
        String username = SecurityUtils.getUsername();
        // 3. 基于当前登录用户, 去查询该用户为候选人/负责人的任务数量
        long total = taskService.createTaskQuery()
                .taskCandidateOrAssigned(username)
                .count();
        info.setTotal(total);
        // 4. 如果数量为 0, 直接返回空数据
        if (total == 0) {
            info.setRows(Collections.emptyList());
            return info;
        }
        // 5. 接收前端的分页条件
        PageDomain pageDomain = TableSupport.buildPageRequest();
        // 5.1. 计算分页开始索引位置 (当前页 - 1) * 每页显示数量
        int startIndex = (pageDomain.getPageNum() - 1) * pageDomain.getPageSize();
        // 6. 基于当前登录用户去查询用户候选人/负责人的任务列表
        List<Task> tasks = taskService.createTaskQuery()
                .taskCandidateOrAssigned(username)
                .orderByTaskCreateTime()
                .asc()
                .listPage(startIndex, pageDomain.getPageSize());
        // 7. 创建集合用于返回代办任务列表
        List<CarPackageAudit> taskList = new ArrayList<>();
        // 8. 遍历任务列表
        for (Task task : tasks) {
            // 8.1. 获取该任务的流程实例 id, 查询流程实例对象, 得到业务标识
            ProcessInstance instance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(task.getProcessInstanceId())
                    .singleResult();
            // 8.2. 通过业务标识, 查询审核记录对象, 并将审核对象转换为 vo 对象
            String businessKey = instance.getBusinessKey();
            CarPackageAudit audit = carPackageAuditService.getById(Long.valueOf(businessKey));
            // 实际需要的对象
            CarPackageAuditVo vo = new CarPackageAuditVo();
            // 将源对象中的属性名的值复制到目标对象中跟源对象属性名一致的属性中
            BeanUtils.copyBeanProp(vo, audit);
            // 8.3. 查询审核记录创建人
            SysUser createUser = userService.selectUserById(Long.valueOf(audit.getCreatorId()));
            vo.setCreateName(createUser.getNickName());
            // 8.4. 为审核记录设置当前任务名称/id, 当前审核人
            vo.setTaskId(task.getId());
            vo.setTaskName(task.getName());
            // 8.5. 通过任务 key 查询候选人列表, 并将用户列表转换为用户名列表, 设置到 vo 对象中
            List<SysUser> sysUsers = bpmnNodeService.selectCandidateByTaskKey(task.getTaskDefinitionKey());
            List<String> userNames = sysUsers.stream().map(SysUser::getNickName)
                    .collect(Collectors.toList());
            vo.setAuditors(JSON.toJSONString(userNames));
            // 8.6. 将当前审核记录对象放入返回的集合中
            taskList.add(vo);
        }
        // 9. 最终返回审核记录列表集合
        info.setRows(taskList);
        return info;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void reapply(Long id, String taskId) {
        // 1. 基于审核记录id查询审核记录对象
        CarPackageAudit audit = carPackageAuditService.getById(id);
        // 2. 验证当前状态是否是审核拒绝, 如果不是审核拒绝, 直接抛出异常
        if (!CarPackageAudit.PACKAGE_AUDIT_STATUS_REJECTED.equals(audit.getStatus())) {
            throw new ServiceException("非审核拒绝不能重新提交申请");
        }
        // 3. 基于任务 id + 当前用户名查询任务对象, 确认是否是审核人（通过是有任务来判断是否是审核人）
        String username = SecurityUtils.getUsername();
        Task task = taskService.createTaskQuery()
                .taskId(taskId + "")
                .taskCandidateOrAssigned(username)
                .singleResult();
        if (task == null) {
            throw new ServiceException("你不是该任务的负责人");
        }
        // 5. 如果能查到, 领取并完成任务, 同时设置流程变量
        taskService.claim(task.getId(), username);
        taskService.complete(taskId);

        // 6. 查询下一个代办任务
        Task nextTask = taskService.createTaskQuery()
                .processInstanceId(task.getProcessInstanceId())
                .singleResult();
        if(nextTask == null) {
            throw new ServiceException("出现错误了");
        }
        // 设置流程变量
        taskService.setVariable(nextTask.getId(), "money", audit.getServiceItemPrice().longValue());
        // 7. 基于该任务查询候选人列表
        List<SysUser> sysUsers = bpmnNodeService.selectCandidateByTaskKey(nextTask.getTaskDefinitionKey());
        for (SysUser user : sysUsers) {
            // 8. 为该任务设置候选人
            taskService.addCandidateUser(nextTask.getId(), user.getUserName());
        }
        // 9. 重新将服务项/审核记录的状态修改为审核中
        serviceItemService.changeStatus(audit.getServiceItemId(), ServiceItem.AUDITSTATUS_AUDITING);
        audit.setStatus(CarPackageAudit.PACKAGE_AUDIT_STATUS_PENDING);
        carPackageAuditService.updateById(audit);


    }
}
